#include <sys/time.h>
#include <unistd.h>

#include <libubus.h>

static struct ubus_context *ctx;
static struct blob_buf b;
static int tx_count = 0;
static int rx_count = 0;

enum
{
    RETURN_CODE,
    RETURN_DATA,
    __RETURN_MAX,
};

static const struct blobmsg_policy return_policy[__RETURN_MAX] = {
    [RETURN_CODE] = {.name = "rc", .type = BLOBMSG_TYPE_INT32},
    [RETURN_DATA] = {.name = "data", .type = BLOBMSG_TYPE_STRING},
};

static void device_read_cb(struct ubus_request *req,
                           int type, struct blob_attr *msg)
{
    struct blob_attr *tb[__RETURN_MAX];
    int rc;
    char *data;

    blobmsg_parse(return_policy, __RETURN_MAX, tb, blob_data(msg), blob_len(msg));

    if (!tb[RETURN_CODE] || !tb[RETURN_DATA])
    {
        fprintf(stderr, "No return code or data received\n");
        return;
    }

    rc = blobmsg_get_u32(tb[RETURN_CODE]);
    data = blobmsg_get_string(tb[RETURN_DATA]);

    rx_count++;
    fprintf(stderr, "read device result: %d, data: %s, tx: %d, rx: %d\n", rc, data, tx_count, rx_count);
    if (tx_count != rx_count)
        fprintf(stderr, "------------------------------------------------------------ tx: %d, rx: %d\n", tx_count, rx_count);
}

static void device_write_cb(struct ubus_request *req,
                            int type, struct blob_attr *msg)
{
    struct blob_attr *tb[__RETURN_MAX];
    int rc;

    blobmsg_parse(return_policy, __RETURN_MAX, tb, blob_data(msg), blob_len(msg));
    if (!tb[RETURN_CODE])
    {
        fprintf(stderr, "No return code received\n");
        return;
    }

    rc = blobmsg_get_u32(tb[RETURN_CODE]);

    fprintf(stderr, "write device result: %d\n", rc);
}

static void request_service(void)
{
    static struct ubus_request req;
    uint32_t id;

    if (ubus_lookup_id(ctx, "device", &id))
    {
        fprintf(stderr, "Failed to look up 'device object\n");
        return;
    }

    /* write data to device */
    /* json:
     *    {
     *         "devicename": "device0",
     *         "fieldname": "version",
     *         "data": "1.0.0"
     *    }
     */
    blob_buf_init(&b, 0);
    blobmsg_add_string(&b, "devname", "device0");
    blobmsg_add_string(&b, "fieldname", "version");
    blobmsg_add_string(&b, "data", "1.0.0");

    ubus_invoke(ctx, id, "write", b.head, device_write_cb, NULL, 1000);

    fprintf(stderr, "write device 'device0' field 'version' with data '1.0.0', done!\n");

    /* read data from device */
    /* json:
     *    {
     *         "devicename": "device0",
     *         "fieldname": "version"
     *    }
     */
    blob_buf_init(&b, 0);
    blobmsg_add_string(&b, "devname", "device0");
    blobmsg_add_string(&b, "fieldname", "version");

    ubus_invoke(ctx, id, "read", b.head, device_read_cb, NULL, 1000);

    fprintf(stderr, "read device 'device0' field 'version' done\n");
}

static void request_timer_event_cb(struct uloop_timeout *timeout)
{
    tx_count++;
    request_service();
    uloop_timeout_set(timeout, 10);
}

static struct uloop_timeout request_timer = {
    .cb = request_timer_event_cb,
};

int main(int argc, char **argv)
{
    const char *ubus_socket = NULL;

    uloop_init();

    ctx = ubus_connect(ubus_socket);
    if (!ctx)
    {
        fprintf(stderr, "Failed to connect to ubus\n");
        return -1;
    }

    ubus_add_uloop(ctx);

    uloop_timeout_set(&request_timer, 1000);

    uloop_run();
    ubus_free(ctx);
    uloop_done();

    return 0;
}
